/*****************************************************************************/
/**
*
* @file ugpiops.c
*
* This file contains user defined gpio related functions.
*
* Ver   Who    Date   	Changes
* ----- ---- ---------- -------------------------------------------------------
* 0.01  abu  12/12/2018 First release
* </pre>
*
******************************************************************************/


/***************************** Include Files *********************************/
#include "ugpiops.h"

/************************** Constant Definitions *****************************/

/**************************** Type Definitions *******************************/

/***************** Macros (Inline Functions) Definitions *********************/

/************************** Variable Definitions *****************************/

/************************** Function Prototypes ******************************/
void uGpioPs_Init(XGpioPs *XGpioPs_Ptr, XGpioPs_Config *XGpioPs_Config_Ptr)
{
	XGpioPs_Config_Ptr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
	XGpioPs_CfgInitialize(XGpioPs_Ptr, XGpioPs_Config_Ptr,
			   XGpioPs_Config_Ptr->BaseAddr);
}

void uGpioPs_Output_Init(XGpioPs * XGpioPs_Ptr, uGpioPs_Output *uGpioPs_Output_Ptr)
{
	XGpioPs_SetDirectionPin(XGpioPs_Ptr, uGpioPs_Output_Ptr->PinNum, uGpioPs_Dir_Out);
	XGpioPs_WritePin(XGpioPs_Ptr, uGpioPs_Output_Ptr->PinNum, uGpioPs_Output_Ptr->PowerOnState);
	XGpioPs_SetOutputEnablePin(XGpioPs_Ptr, uGpioPs_Output_Ptr->PinNum, uGpioPs_Out_Enable);
}

/****************************************************************************/
/**
*
* This function is used for setting the Interrupt Type, Interrupt Polarity and
* Interrupt of Any specified GPIO pin.
* Function Prototype:
* void XGpioPs_SetIntrType(XGpioPs *InstancePtr, u8 Bank, u32 IntrType,
*			  u32 IntrPolarity, u32 IntrOnAny)
*
* @param	InstancePtr is a pointer to an XGpioPs instance.
* @param	uGpioPs_Input_Ptr is a pointer to an uGpioPs_Input instance.
*=========================================================
* @variable		IntrType is the 32 bit mask of the interrupt type.
*		0 means Level Sensitive and 1 means Edge Sensitive.
* @variable		IntrPolarity is the 32 bit mask of the interrupt polarity.
*		0 means Active Low or Falling Edge and 1 means Active High or
*		Rising Edge.
* @variable		IntrOnAny is the 32 bit mask of the interrupt trigger for
*		edge triggered interrupts. 0 means trigger on single edge using
*		the configured interrupt polarity and 1 means  trigger on both
*		edges.
*=========================================================
* @return	None.
*
* @note		In the prototype function, function XGpioPs_SetPinIntrType() is named,
*		but found nowhere. Hence the user defined function is here to replace it.
*
*****************************************************************************/
void uGpioPs_SetIntrTypePin(XGpioPs *InstancePtr, uGpioPs_Input *uGpioPs_Input_Ptr)
{
	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
	Xil_AssertVoid(uGpioPs_Input_Ptr->PinNum < InstancePtr->MaxPinNum);
	Xil_AssertVoid(uGpioPs_Input_Ptr->IntrEnable == InterruptEnable);
	Xil_AssertVoid(uGpioPs_Input_Ptr->Intr.IntrType != IntrNull);

	u8 PinBank, PinNumInBank;
	u32 IntrRegTmp;
	u32 IntrType, IntrPolarity, IntrOnAny;

	switch(uGpioPs_Input_Ptr->Intr.IntrType){
		case IntrHigh:			IntrType = 0; IntrPolarity = 1; IntrOnAny = 0; break;
		case IntrLow:			IntrType = 0; IntrPolarity = 0; IntrOnAny = 0; break;
		case IntrRisingEdge:	IntrType = 1; IntrPolarity = 1; IntrOnAny = 0; break;
		case IntrFallingEdge:	IntrType = 1; IntrPolarity = 0; IntrOnAny = 0; break;
		case IntrAnyEdge:		IntrType = 1; IntrPolarity = 0; IntrOnAny = 1; break;
		}
	
	XGpioPs_GetBankPin(uGpioPs_Input_Ptr->PinNum, &PinBank, &PinNumInBank);

	IntrRegTmp = XGpioPs_ReadReg(InstancePtr->GpioConfig.BaseAddr, 
		((u32)(PinBank) * XGPIOPS_REG_MASK_OFFSET) + XGPIOPS_INTTYPE_OFFSET);
	XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
			  ((u32)(PinBank) * XGPIOPS_REG_MASK_OFFSET) +
			  XGPIOPS_INTTYPE_OFFSET, ((u32)(IntrType << PinNumInBank)) & IntrRegTmp);
	
	IntrRegTmp = XGpioPs_ReadReg(InstancePtr->GpioConfig.BaseAddr, 
		((u32)(PinBank) * XGPIOPS_REG_MASK_OFFSET) + XGPIOPS_INTPOL_OFFSET);
	XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
			  ((u32)(PinBank) * XGPIOPS_REG_MASK_OFFSET) +
			  XGPIOPS_INTPOL_OFFSET, ((u32)(IntrPolarity << PinNumInBank)) & IntrRegTmp);

	IntrRegTmp = XGpioPs_ReadReg(InstancePtr->GpioConfig.BaseAddr, 
		((u32)(PinBank) * XGPIOPS_REG_MASK_OFFSET) + XGPIOPS_INTANY_OFFSET);
	XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
			  ((u32)(PinBank) * XGPIOPS_REG_MASK_OFFSET) +
			  XGPIOPS_INTANY_OFFSET, ((u32)(IntrOnAny << PinNumInBank)) & IntrRegTmp);
}

/*****************************************************************************/
/**
*
* This function initializes the input gpios, including direction, interrupt 
* enable, interrupt type, interrupt priority
*
* @param	XGpioPs *XGpioPs_Ptr, pointer to the XGpioPs instance.
*			uGpioPs_Input *uGpioPs_Key_Ptr, pointer to the uGpioPs_Input instance
*
* @return	None.
*
* @note		Process of setting GPIO interrupts
*			1. set as input
*			2. disable gpio interrupt
*			3. set interrupt type, level or edge
*			4. set interrupt polarity, high level/rising edge or low level/falling edge
*			5. set edge type, single edge or any edge
*			6. set handler function
*
******************************************************************************/

u8 uGpioPs_Input_Init(XGpioPs *XGpioPs_Ptr, uGpioPs_Input *uGpioPs_Input_Ptr)
{
	
	XGpioPs_SetDirectionPin(XGpioPs_Ptr, uGpioPs_Input_Ptr->PinNum, uGpioPs_Dir_In);

	//if the gpio is not configured as interrupt, then finish initialization and return
	if(uGpioPs_Input_Ptr->IntrEnable == InterruptDisable){
		DEBUG("GPIO pin %d initialized as input without interrupt success\n\r",
					uGpioPs_Input_Ptr->PinNum);
		return 1;
		}
	
	XGpioPs_IntrDisablePin(XGpioPs_Ptr, uGpioPs_Input_Ptr->PinNum);
	uGpioPs_SetIntrTypePin(XGpioPs_Ptr, uGpioPs_Input_Ptr);
	XGpioPs_IntrEnablePin(XGpioPs_Ptr, uGpioPs_Input_Ptr->PinNum);

	DEBUG("GPIO pin %d initialized as input with interrupt tye ", uGpioPs_Input_Ptr->PinNum);
	switch(uGpioPs_Input_Ptr->Intr.IntrType){
		case IntrHigh:			DEBUG("High Level\n\r"); break;
		case IntrLow:			DEBUG("Low Level\n\r"); break;
		case IntrRisingEdge:	DEBUG("Rising Edge\n\r"); break;
		case IntrFallingEdge:	DEBUG("Falling Edge\n\r"); break;
		case IntrAnyEdge:		DEBUG("Any Edge\n\r"); break;
		}

	
}

/*****************************************************************************/
/**
*
* This function enables IRQ interrupt in register cpsr,
* and can be replaced by Xil_ExceptionEnable() in "xil_exception.h",
* and it is better to use MACRO Xil_ExceptionEnable() instead of this FUNCTION
*
* @param	None.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/

void uPs_EnableIRQ(void)
{
	__asm__ __volatile__(
		"msr	cpsr, %0\n"::
		"r"(
				(
					{unsigned int val;							//get the current value of cpsr,
						__asm__ __volatile__					//then AND it with 0xef in order 
						("mrs	%0, cpsr\n":"=r" (val));val;	//to set I to 0 while keep other
					}											//bits unchanged
				) & ~(0x80 & (0x40 | 0x80))			//this line can be changed to ") & 0xffffffef"
			)
		);
}

/*****************************************************************************/
/**
* Handler for GPIO interrupts, IRQ ID is 52
* 
*
* @param	None.
*
* @return	None.
*
* @note		Procedure
			1. Disable gpio interrupt
			2. Clear gpio interrupt
			3. Get gpio number
			4. Call correspond handler
			5. Enable gpio interrupt
*
******************************************************************************/

Xil_InterruptHandler uGpioPsIntrHandler(void *Data)
{

}


